<template>
    <el-dialog
            :title="paramId ? `${tempParams.paramName}` : ''"
            :visible.sync="visible"
            append-to-body
            @close="close"
            width="700px">
            <div v-loading="ployfitLoading || loading" class="container">
                <template v-if="!paramId">
                    <div class="form-row flex-row flex-item">
                        <div class="label">所属分类</div>
                        <el-cascader
                                style="width: 550px"
                                size="mini"
                                v-model="deviceTypes"
                                @change="getQuotaByDeviceType"
                                :options="deviceTypeList"
                                :props="{
                            value: 'key',
                            label: 'keyName'
                        }"></el-cascader>
                    </div>
                    <div class="form-row flex-row flex-item" v-if="deviceTypes.length">
                        <div class="label">指标名称</div>
                        <el-select
                                style="width: 550px"
                                size="mini"
                                v-model="tempParams.paramName"
                                @change="getRangeTwenty"
                                placeholder="请选择需要添加的指标">
                            <el-option
                                    v-for="item in deviceNameList"
                                    :key="item.value"
                                    :label="item.value"
                                    :disabled="item.disabled"
                                    :value="item.value">
                            </el-option>
                        </el-select>
                    </div>
                </template>
                <div v-if="tempParams.paramName" id="vals-chart-container"></div>
                <div class="form-row flex-row flex-item">
                    <div class="label">权 重</div>
                    <el-input style="width: 100px"
                              size="mini"
                              v-model="tempParams.weightRatio"
                              @change="(val) => numInput('weightRatio', val)" placeholder="请输入权重系数">
                    </el-input>
                    <div class="tips" style="margin: 0 0 0 10px">{{['轻微','较轻微','一般','较重要','重要'][Math.floor((tempParams.weightRatio - 1) / 2)]}}</div>
                </div>
                <div class="tips">
                    <i class="el-icon-warning-outline"></i>
                    评估报告中该指标的影响值，计算方式：当前权重/报告中所有指标权重之和
                </div>
                <div class="form-row flex-row flex-item">
                    <div class="label">指标备注</div>
                    <el-input style="width: 550px" v-model="tempParams.remarks" size="mini" placeholder="请输入指标备注"></el-input>
                </div>
                <div class="tips">
                    <i class="el-icon-warning-outline"></i>
                    选填
                </div>
                <div class="form-row flex-row flex-item">
                    <div @click="copyToClipboard" class="label" style="flex-shrink: 0">拟合区间</div>
                    <el-input size="mini"
                              :disabled="isEnum"
                              v-model="tempParams.minValue"
                              @change="(val) => numInput('minValue', val)"
                              placeholder="拟合开始值"></el-input>
                    <span style="padding: 10px">至</span>
                    <el-input size="mini"
                              :disabled="isEnum"
                              v-model="tempParams.maxValue"
                              @change="(val) => numInput('maxValue', val)"
                              placeholder="拟合结束值"></el-input>
                </div>
                <el-input v-if="env === 'development'" size="mini" v-model="coordStr" @blur="strToCoordinate"></el-input>
                <el-input v-if="env === 'development'" size="mini" v-model="testRate"></el-input>
                <input type="text" style="position: absolute; z-index: -1; opacity: 0" id="code_copy"/>
                <div class="tips">
                    <i class="el-icon-warning-outline"></i>
                    <span v-if="isEnum">检测到输入坐标为枚举值，无需拟合区间</span>
                    <span v-else>拟合区间范围内才会调用拟合公式进行计算</span>
                </div>
                <div class="form-row flex-row flex-item">
                    <div class="label">坐标</div>
                    <div>
                        <el-table
                                style="width: 550px"
                                size="mini"
                                border
                                :data="coordinates"
                        >
                            <el-table-column
                                    prop="date"
                                    label="指标值">
                                <el-input slot-scope="scope" size="mini" placeholder="不输入则为空值" @blur="inputValue" v-model="scope.row.value"></el-input>
                            </el-table-column>
                            <el-table-column
                                    prop="name"
                                    label="指标得分">
                                <el-input slot-scope="scope" size="mini" placeholder="请输入得分" v-model="scope.row.score" @change="(val) => inputScore(scope.$index, val)"></el-input>
                            </el-table-column>
                            <el-table-column
                                    prop="columnProp"
                                    label="操作">
                                <el-button slot-scope="scope" type="text" size="mini" @click="deleteCoordinate(scope)">删除</el-button>
                            </el-table-column>
                        </el-table>
                        <el-button type="text" size="mini" @click="addCoordinate">添加坐标</el-button>
                    </div>
                </div>
                <div class="tips">
                    <p v-if="paramsRange.length">指标参考值：{{paramsRange.join(',')}}</p>
                    <p><i class="el-icon-warning-outline"></i>如果指标值均为数字，系统将插值出拟合区间内所有指标值的打分，如果为枚举，则需输入每个枚举的打分</p>
                </div>
                <br>
                <div :style="{opacity: tempParams.quotaFormula ? 1 : 0}" class="flex1" id="function_curve"></div>
                <br>
                <div class="flex-column">
                    <div v-if="tempParams.quotaFormula" class="flex-row">
                        <div class="label">计算公式</div>
                        <div class="code flex1">{{tempParams.quotaFormula}}</div>
                    </div>
                </div>
            </div>
            <span slot="footer" class="dialog-footer">
                <el-button size="small" @click="close">取 消</el-button>
                <el-button :loading="ployfitLoading" size="small" type="primary" @click="submitForm">保 存</el-button>
              </span>
    </el-dialog>
</template>

<script>
import { mapState } from 'vuex'
import polyfit from '@/utils/polyfit'
const echarts = require('echarts/lib/echarts')
export default {
  name: 'ployfit',
  props: {
    paramId: {
      required: true
    }
  },
  data() {
    return {
      visible: false,
      loading: false,
      ployfitLoading: false,
      tempParams: {
        id: '',
        minValue: 0,
        maxValue: 100,
        weightRatio: 1,
        quotaFormula: '',
        paramName: '',
        rate: 1
      },
      coordinates: [],
      inputMatrix: [],
      deviceTypes: [],
      deviceNameList: [],
      paramsRange: [],
      errorMsg: '',
      // 本地用
      rangeStrCopy: '',
      coordStr: '',
      testRate: 1,
      env: ''
    }
  },
  watch: {
    coordinates: {
      handler(N){
        this.coordStr = N.map(item => `(${item.value}, ${item.score})`).join('，')
      },
      immediate: true
    }
  },
  computed: {
    ...mapState('dict', ['deviceTypeList']),
    isEnum() {
      return this.coordinates.some(item => {
        return isNaN(Number(item.value))
      })
    },
    isIncremental() {
      const list = this.coordinateToArray()
      const end = list[0]
      const start = list[list.length - 1]
      if (end && start && !this.isEnum) {
        return (end.y - start.y) / (end.x - start.x) > 0
      }
      return false
    }
  },
  mounted() {
    this.env = process.env.NODE_ENV
    this.visible = true
    if (this.paramId) {
      this.getById(this.paramId)
    } else {

      this.tempParams = {
        id: '',
        minValue: 0,
        maxValue: 100,
        weightRatio: 1,
        quotaFormula: '',
        deviceType: '',
        deviceTypeTwo: '',
        rate: 1,
      }
      this.coordinates = []
      this.deviceTypes = []
    }
  },
  methods: {
    polyfit,
    // 本地写文档用
    copyToClipboard() {
      if (this.env !== 'development') {return}
      try {
        const inputElement = document.querySelector('#code_copy'); // 选中元素
        inputElement.value = this.rangeStrCopy
        inputElement.select(); // 选中输入框
        document.execCommand('copy'); // 进行复制
      } catch (err) {
        console.log(err)
      }
    },
    // 本地写文档用
    strToCoordinate() {
      const strs = this.coordStr.replace(/\)/g, '').replace(/\(/g, '').split('，')
      this.coordinates = strs.map((item) => {
        return {
          value: item.split(', ')[0] * this.testRate,
          score: item.split(', ')[1]
        }
      })
      this.tempParams.maxValue = this.coordinates[0].value
      this.tempParams.minValue = this.coordinates[this.coordinates.length - 1].value
      this.startDraw()
    },
    close() {
      this.visible = false
      this.$emit('close')
    },
    getQuotaByDeviceType() {
      this.tempParams = {
        ...this.tempParams,
        deviceType: this.deviceTypes[0],
        deviceTypeTwo: this.deviceTypes[1],
        paramName: ''
      }
      this.paramsRange = []
      this.$http('/knowledge/getQuotaByDeviceType', {
        params: {
          deviceType: this.deviceTypes[0],
          deviceTypeTwo: this.deviceTypes[1]
        }
      }).then((res) => {
        const { exists, notExists } = res
        this.deviceNameList = [
          ...notExists.map(item => {
            return {
              value: item,
              disabled: false
            }
          }),
          ...exists.map(item => {
            return {
              value: item,
              disabled: true
            }
          })
        ]
      })
    },
    getRangeByDeviceType() {
      this.paramsRange = []
      this.$http('/knowledge/getRangeByDeviceType', {
        params: {
          deviceType: this.deviceTypes[0],
          deviceTypeTwo: this.deviceTypes[1],
          paramName: this.tempParams.paramName
        }
      }).then(res => {
        this.paramsRange = res.sort((a, b) => {
          return a - b
        })
      }).catch(err => {
        this.$TZmessage.error(err.msg || '获取指标的参考值失败')
      })
    },
    inputScore(index, val) {
      const score = parseFloat(val) || 0
      this.coordinates = this.coordinates.map((item, i) => {
        if (i === index){
          item.score = score < 0 ? 0 : score > 100 ? 100 : score
        }
        return item
      })
      this.startDraw()
    },
    inputValue() {
      this.$nextTick(() => {
        this.coordinates = this.coordinates.sort((a, b) => {
          return b.value - a.value
        })
        if (!this.isEnum) {
          // const values = this.coordinates.map(item => item.value)
          // this.tempParams.minValue = Math.min(...values)
          // this.tempParams.maxValue = Math.max(...values)
        }
        this.startDraw()
      })
    },
    numInput(key, val) {
      if (key === 'weightRatio') {
        const rate = Number(val) || 1
        this.tempParams.weightRatio = rate < 1 ? 1 : rate > 10 ? 10 : rate
      }
      // let defaultVal = parseFloat(val) || 0
      // defaultVal = defaultVal < min ? min : defaultVal > max ? max : defaultVal
      // if (key === 'minValue' && defaultVal > this.tempParams.maxValue) {
      //     this.tempParams.maxValue = defaultVal
      // }
      // if (key === 'maxValue' && defaultVal < this.tempParams.minValue) {
      //     this.tempParams.minValue = defaultVal
      // }
      // this.tempParams = {
      //     ...this.tempParams,
      //     [key]: defaultVal
      // }
      this.startDraw()
    },
    addCoordinate() {
      this.coordinates.push({
        value: '',
        score: 100
      })
    },
    deleteCoordinate(row) {
      this.coordinates.splice(row.$index, 1)
      this.inputValue()
    },
    // open(params) {
    //     this.visible = true
    //     this.paramId = params.id || ''
    //     if (this.paramId) {
    //         this.getById(this.paramId)
    //     } else {
    //
    //         this.tempParams = {
    //             id: '',
    //             minValue: 0,
    //             maxValue: 100,
    //             weightRatio: 1,
    //             quotaFormula: '',
    //             deviceType: '',
    //             deviceTypeTwo: '',
    //         }
    //         this.coordinates = []
    //         this.deviceTypes = []
    //     }
    // },
    getRangeTwenty() {
      this.loading = true
      const {paramName, deviceType, deviceTypeTwo} = this.tempParams
      this.$http.get('/knowledge/getRangeTwenty', {
        params: {
          paramName,
          deviceType,
          deviceTypeTwo
        }
      }).then(res => {
        let isEnum = false
        let list = (res || []).map(item => {
          let label = 0
          isEnum = item.isEnum
          let rangeArray = []
          try {
            if (item.isEnum) {
              label = item.range || '(空值)'
            } else {
              rangeArray = item.range.replace(/\[/, '')
                .replace(/\]/, '')
                .replace(/\)/, '')
                .split(',').map(str => {
                  return Number(str)
                })
              const sum = rangeArray.reduce((sum, cur) => {
                return sum + cur
              }, 0)
              label = (Math.round(sum / 0.02) / 100).toFixed(2)
            }
          } catch (e) {
            console.log(e)
          }
          return {
            ...item,
            label,
            rangeArray,
            value: item.num
          }
        })
        list = list.filter((item, index) => {
          return (item.label !== list[0]?.label) || !index
        })
        const total = list.reduce((sum, item) => {
          return item.value + sum
        }, 0)
        list = list.map(item => {
          return {
            ...item,
            rate: (item.value * 100 / total).toFixed(2)
          }
        })
        this.rangeStrCopy = list.length ? `[${list[0]?.rangeArray[0]}, ${list[list.length - 1].rangeArray[1]}]` : ''
        this.drawChart('vals-chart-container', this.getLineOption({
          yAxis_name: '在该值附近出现次数',
          xAxis_name: '历史值',
          title: (isEnum || list.length === 0) ? '历史值分布频次' : `历史值分布频次（${list[0]?.rangeArray[0]}~${list[list.length - 1].rangeArray[1]}）`
        }, list))
      }).catch(err => {
        console.log(err)
        this.$TZmessage.error(err.msg || '获取分布范围失败')
      }).finally(() => {
        this.loading = false
      })
    },
    // 兼容老数据
    strToArray(input_points) {
      try {
        let input_array = input_points.split('\n');
        let tmp = null;
        let point_array = [];
        for (let i = 0; i < input_array.length; i++) {
          tmp = input_array[i].split(',')
          if (tmp && tmp.length === 2) {
            point_array.push({'value': parseFloat(tmp[0].trim()), 'score': parseFloat(tmp[1].trim())});
          }
        }
        return point_array;
      } catch(e) {
        return false;
      }
    },
    getById(id) {
      if (!id) {return}
      this.ployfitLoading = true
      this.$http.get('/knowledge/getParamsById', {
        params: {
          id
        }
      }).then(res => {
        this.tempParams = res || {}
        const { coordinate } = this.tempParams
        const {deviceType, deviceTypeTwo} = this.tempParams
        this.deviceTypes = [deviceType, deviceTypeTwo]
        // this.getRangeByDeviceType()
        this.getRangeTwenty()
        try{
          this.coordinates = JSON.parse(coordinate).sort((a, b) => {
            return b.value - a.value
          })
        } catch (e) {
          // 兼容老数据
          this.coordinates = this.strToArray(coordinate).sort((a, b) => {
            return b.value - a.value
          })
        }
        this.startDraw()
      }).catch(err => {
        this.$TZmessage.error(err.msg || '获取指标参数失败')
      }).finally(() => {
        this.ployfitLoading = false
      })
    },
    submitForm() {
      if (this.errorMsg) {
        this.$alert(this.errorMsg)
        return;
      }
      const { minValue, maxValue } = this.tempParams
      if (Number(maxValue) < Number(minValue)) {
        this.$TZmessage.error('拟合区间开始值不能大于结束值')
        return;
      }
      if (!this.tempParams.quotaFormula) {
        this.$TZmessage.warning('计算公式不可为空，请点击左侧“开始拟合”按钮生成公式')
        return
      } else if (!this.coordinates?.length) {
        this.$TZmessage.warning('拟合坐标不可为空，请点击在左侧输入框中输入关键点坐标')
        return;
      }
      if (this.tempParams.id) {
        this.updateById()
      } else {
        if (!this.tempParams.paramName) {
          this.$TZmessage.warning('请输入指标名称')
          return;
        }
        if (!this.deviceTypes?.length) {
          this.$TZmessage.warning('请选择所属类型')
          return;
        }
        this.addQuota()
      }
    },
    addQuota() {
      this.$http.post('/knowledge/addQuota', { ...this.tempParams,
        coordinate: JSON.stringify(this.coordinates),
        deviceType: this.deviceTypes[0],
        deviceTypeTwo: this.deviceTypes[1],
        isIncremental: this.isIncremental ? 1 : 0,
      }).then(() => {
        this.$TZmessage.success('添加成功')
        this.close()
      }).catch(err => {
        this.$TZmessage.error(err.msg || '添加失败')
      }).finally(() => {
        this.ployfitLoading = false
        this.$emit('refresh')
      })
    },
    updateById() {
      this.ployfitLoading = true
      this.$http.post('/knowledge/updateParams', {
        ...this.tempParams,
        isEnum: this.isEnum ? 1 : 0,
        isIncremental: this.isIncremental ? 1 : 0,
        coordinate: JSON.stringify(this.coordinates)
      // }).then(res => {
      }).then(() => {
        this.$TZmessage.success('修改成功')
        this.close()
      }).catch(err => {
        this.$TZmessage.error(err.msg || '修改指标参数失败')
      }).finally(() => {
        this.ployfitLoading = false
        this.$emit('refresh')
      })
    },
    coordinateToArray() {
      return this.coordinates.map(item => {
        return {
          x: item.value === '' ? '(空值)' : item.value,
          y: item.score
        }
      })
    },
    startDraw() {
      this.errorMsg = ''
      const { minValue, maxValue } = this.tempParams
      let x_data = []
      let y_data = []
      const max = maxValue || 100
      let points = this.coordinateToArray()
      if (points.length === 0) {
        points = [{x:0, y: 0}]
      }
      if (this.isEnum) {
        this.tempParams.quotaFormula = '1*x'
        x_data = points.map(item => {
          return item.x
        })
        y_data = points.map(item => {
          return item.y
        })
      } else {
        // 连续数据必须多个坐标
        if (this.coordinates.length < 2) {return;}
        this.tempParams.quotaFormula = this.polyfit(points)
        if (!this.tempParams.quotaFormula) {
          this.$TZmessage.error('当前坐标无法使用多项式进行拟合，请检查输入值是否合理')
          return
        }
        let val = (Number(minValue) || 0)
        let i = 0
        const step = (max - val) / 60
        while (val < max && i < 60) {
          i++
          val += step
          const x_val = Math.round(val * 1000) / 1000
          x_data.push(x_val)
          let y_val = 0
          try {
            y_val = eval(this.tempParams.quotaFormula.replace(/x/g, val)).toFixed(2)
          } catch (e) {
            console.log(e)
          }
          y_data.push(y_val)
          // 改为抛错
          // if (y_data.some(item => item < 0 || item > 100)) {
          //     this.errorMsg = '坐标不合理，拟合得分超出了0~100的范围，请调整坐标和拟合区间'
          //     // throw '坐标不合理，拟合得分超出了0~100的范围'
          // }
          y_data = y_data.map(item => {
            return item > 100 ? 100 : item < 0 ? 0 : item
          })
        }
      }
      const list = x_data.map((label, index) => {
        return {
          label,
          value: y_data[index]
        }
      })
      this.drawChart('function_curve', this.getLineOption({
        yAxis_name: '打分',
        xAxis_name: '指标',
        title: '拟合曲线'
      }, list))
    },
    getLineOption(textConfig, list) {
      const {yAxis_name, xAxis_name, title} = textConfig
      const max = Math.max(...list.map(item => item.value))
      const maxLength = `${max}`.length
      const leftPadding = `${12 * maxLength}px`
      return {
        title: {
          text: title,
          subtext: ''
        },
        tooltip: {
          trigger: 'axis',
          formatter: (params) => {
            const index = params[0].dataIndex
            const item = list[index]
            if (item.rate) {
              return `${item.range}范围<br/>出现：${item.value}次<br/>占比：${item.rate}%`
            } else {
              return `指标值：${item.label}<br/>指标打分：${item.value}`
            }
          }
        },
        grid: {
          left: leftPadding,
          x: 60,
          y: 60,
          x2: 55,
          y2: 25
        },
        toolbox: {
          show: true,
          feature: {
            mark: {show: true},
            dataZoom: {show: true},
            dataView: {show: true, readOnly: false},
            magicType: {show: true, type: ['line', 'bar']},
            restore: {show: true},
            saveAsImage: {show: true}
          }
        },
        xAxis: [{
          type: 'category',
          boundaryGap: true,
          name: xAxis_name,
          data: list.map(item => item.label)
        }],
        yAxis: [{
          type: 'value',
          scale: true,
          name: yAxis_name,
          axisLabel: {
            formatter: '{value}'
          }
        }],
        series: [{
          name: yAxis_name,
          type: 'line',
          data: list.map(item => item.value),
          markPoint: {
            data: []
          }
        }]
      };
    },
    drawChart(e_id, option) {
      setTimeout(() => {
        const chartContainer = document.getElementById(e_id);
        const existingChart = echarts.getInstanceByDom(chartContainer);
        existingChart?.dispose();
        let eChart = echarts.init(chartContainer);
        eChart.setOption(option);
      }, 500)
      // return eChart
    },
    // polyfit(inputArray) {
    //   const { maxValue, minValue } = this.tempParams
    //   const vals = [Math.abs(maxValue), Math.abs(minValue)]
    //   const maxVal = Math.max(...vals)
    //   const rateVal = maxVal > 1000 ? maxVal / 100 : 1
    //   const userInput = inputArray.map(item => {
    //     return {
    //       ...item,
    //       x: item.x / rateVal
    //     }
    //   })
    //   let returnResult = [];
    //   this.inputMatrix = [];
    //   let n = userInput.length;
    //   for (let i = 0; i < n; i++) {
    //     let tempArr = [];
    //     for (let j = 0; j < n; j++) {
    //       tempArr.push(Math.pow(userInput[i].x, n - j - 1));
    //     }
    //     tempArr.push(userInput[i].y);
    //     this.inputMatrix.push(tempArr);
    //   }
    //   for (let i = 0; i < n; i++) {
    //     let base = this.inputMatrix[i][i];
    //     for (let j = 0; j < n + 1; j++) {
    //       if (base === 0) {
    //         // 存在相同x不同y的点，无法使用多项式进行拟合
    //         return false;
    //       }
    //       this.inputMatrix[i][j] = this.inputMatrix[i][j] / base;
    //     }
    //     for (let j = 0; j < n; j++) {
    //       if (i !== j) {
    //         let baseInner = this.inputMatrix[j][i];
    //         for (let k = 0; k < n + 1; k++) {
    //           this.inputMatrix[j][k] = this.inputMatrix[j][k] - baseInner * this.inputMatrix[i][k];
    //         }
    //       }
    //     }
    //   }
    //   for (let i = 0; i < n; i++) {
    //     if (this.inputMatrix[i][n] > 0 && returnResult.length) {
    //       returnResult.push('+');
    //     }
    //
    //     if (this.inputMatrix[i][n] !== 0) {
    //       let tmp_x = '';
    //       for (let j = 0; j < n - 1 - i; j++) {
    //         tmp_x = tmp_x + '*x';
    //       }
    //       returnResult.push((Math.round(this.inputMatrix[i][n] * 10**9) / 10**9 + tmp_x));
    //     }
    //   }
    //   const res = returnResult.join('')
    //   return rateVal === 1 ? res : res.replace(/x/g, `(x/${rateVal})`);
    // }
  }
}
</script>

<style lang="scss" scoped>
    .container {
        max-height: 60vh;
        overflow: auto;
        .form-row {
            margin-bottom: 5px;
        }
    }
    .tips {
        color: $color-warning;
        font-size: 12px;
        line-height: 16px;
        margin-bottom: 10px;
        opacity: 0.7;
    }
    #function_curve, #vals-chart-container {
        min-height: 250px;
    }
    .label {
        padding: 10px;
        padding-left: 0;
        width: 60px;
    }
    .code {
        background: #000000;
        color: #ffffff;
        padding: 10px;
        border-radius: 5px;
    }
</style>